pragma Ada_2012;
with Interfaces, Interfaces.C;
with System;
package body iconv is

   pragma Linker_Options("-liconv2");

--   function Error return access constant Interfaces.C.int;
--   -- pragma Import(C, Error, "__error");
--   pragma Import(C, Error, "__error");
--
--   function Success return Boolean
--   is
--      use Interfaces.C;
--   begin
--      return Error.all = 0;
--   end Success;

   function Open (
      From_Code, To_Code : String
      ) return iconv_t
   is
      function Invoke (
         fc, tc : String
         ) return iconv_t;
--      pragma Import (C, Invoke, "iconv_open");
      pragma Import (C, Invoke, "libiconv_open");
   begin
      return Invoke(To_Code & AscII.Nul, From_Code & AscII.Nul);
   end Open;

   function Convert (
      cd : iconv_t;
      src : String
      ) return String
   is
      use Interfaces.C;
      function Invoke (
         cd : iconv_t;
         in_buf : access System.Address;
         in_num : access Interfaces.C.size_t;
         to_buf : access System.Address;
         to_num : access Interfaces.C.size_t
         ) return Interfaces.C.size_t;
 --     pragma Import(C, Invoke, "iconv");
      pragma Import(C, Invoke, "libiconv");

      dst : String(1 .. src'Length * 3);

      src_ptr : aliased System.Address;
      src_num : aliased size_t;
      dst_ptr : aliased System.Address;
      dst_num : aliased size_t;
      n : size_t;
   begin
      if src'Length = 0 then
         return "";
      end if;
      src_ptr := src(src'First)'Address;
      src_num := src'Length;
      dst_ptr := dst(1)'Address;
      dst_num := dst'Length;
      while src_num /= 0 loop
         n := Invoke(cd, src_ptr'Access, src_num'Access, dst_ptr'Access, dst_num'Access);
         exit when n > src'Length;
         if n = 0 then
            -- dst space not enougth
            -- realloc dst
            null;
         end if;
      end loop;
      return dst(1 .. Natural(dst'Length - dst_num));
   end Convert;

   procedure Close (ic : iconv_t)
   is
      use Interfaces.C;
      function Invoke (ic : iconv_t) return int;
      -- pragma Import (C, Invoke, "iconv_close");
      pragma Import (C, Invoke, "libiconv_close");
      dummy : int;
   begin
      dummy := Invoke(ic);
   end Close;

   function Create (From, To : String) return Handle
   is
   begin
      return Result : Handle do
         Result.ic := Open (From, To);
      end return;
   end Create;

   function Convert (this : Handle; Source : String) return String
   is
   begin
      return Convert(this.ic, Source);
   end Convert;

   procedure Open (this : in out Handle; From, To : String)
   is
   begin
      this.Close;
      this.ic := Open (From, To);
   end Open;

   procedure Close (this : in out Handle)
   is
      use type System.Address;
   begin
      if System.Address(this.ic) /= System.Null_Address then
         Close(this.ic);
         this.ic := iconv_t(System.Null_Address);
      end if;
   end Close;

   procedure Finalize (this : in out Handle)
   is
   begin
      this.Close;
   end Finalize;
end iconv;
